package controllers

import (
	"encoding/json"
	"github.com/pkg/errors"
	"github.com/spf13/cast"
	"io/ioutil"
	"nft-server/common"
	"nft-server/db"
	"nft-server/errcode"
	"nft-server/logger"
	"nft-server/model"
	"nft-server/service"
	"nft-server/util"
	"path/filepath"
)

const (
	//用户请求铸造，销毁
	RequestMintURI = "/v1/chain/mint/"
	RequestBurnURI = "/v1/chain/burn/"
	BurnAssetURI   = "/v1/chain/burnasset"
	//RequestMultipleMintURI = "/v1/chain/mint/multiple"

	//获取链上全部集合
	GetCollectionOnChain = "/v1/chain/collection"

	//post
	SaveFileURI = "/v1/file/save"
	GetFileURI  = "/v1/file/get"
)

type MintController struct {
	CommonController
}

func (nc *MintController) SaveFile() {

	file, information, err := nc.GetFile("file")
	if err != nil {
		logger.Error("read file error: %v", err)
		nc.HandleErrCode(errcode.E.File.SaveFailed.WithErr(err))
		return
	}

	// 读取上传文件内容, data就是文件的内容
	// ioutil.ReadAll返回[]byte类型数据
	data, err := ioutil.ReadAll(file)

	defer file.Close()

	fileType := ""
	fileExt := filepath.Ext(information.Filename)
	if fileExt == ".jpg" || fileExt == ".png" || fileExt == ".gif" || fileExt == ".jpeg" || fileExt == ".JPG" || fileExt == ".PNG" || fileExt == ".GIF" || fileExt == ".JPEG" {
		fileType = ".png"
	} else {
		logger.Error("SaveFile error: %v", err)
		nc.HandleErrCode(errcode.E.Base.ErrorMessage.WithMessage("仅支持jpg,png,gif,jpeg格式"))
		return
	}

	obsKey, err := service.SaveFile(data, fileType)

	if err != nil {
		logger.Error("SaveFile error: %v", err)
		nc.HandleErrCode(errcode.E.File.SaveFailed.WithErr(err))
	}

	response := &common.FileResponse{}
	response.ObsKey = obsKey
	nc.HandleBusinessObj(response)
}

func (nc *MintController) GetFileUrl() {

	req := common.GetAssetWebUrl{}
	req.ObsKey = nc.Ctx.Input.Query("obsKey")

	assetWebUrl, err := util.GetAssetUrl(req.ObsKey)

	if err != nil {
		logger.Error("GetAssetUrl error: %v", err)
		nc.HandleErrCode(errcode.E.File.SaveFailed.WithErr(err))
		return
	}

	response := &common.FileUrlResponse{}
	response.FileUrl = assetWebUrl
	nc.HandleBusinessObj(response)
}

//对单张图片的信息进行检查,检查req相关参数,userId为已确认过的当前用户id
func (nc *MintController) CheckMintParameter(req common.MintReq, userId int) (collection *model.CollectionDetail, err error) {
	collection, err = service.GetCollectionDetailByUserIdAndCollection(userId, req.CollectionIndex, util.DefaultSubCollectionIndex)
	if err != nil {
		err = errors.Errorf("该分类种类不存在")
		logger.Error(err.Error())
		return nil, err
	}
	if collection.Supplies > 0 {
		err = errors.Errorf("每个藏品种类只能铸造一次")
		return nil, err
	}
	if collection.CollectionStatus != util.CollectionUnMint {
		err = errors.Errorf("只有处于未铸造状态的藏品种类才能进行铸造")
		return nil, err
	}
	//图片名校验
	if len(req.Name) < util.LowerBoundOfFileNameForMint || len(req.Name) > util.UpperBoundOfFileNameForMint {
		err = errors.Errorf("图片名长度为必须为1-100")
		return nil, err
	}
	//图片url校验
	if len(req.FileUrl) < util.LowerBoundOfFileUrlForMint || len(req.FileUrl) > util.UpperBoundOfFileUrlForMint {
		err = errors.Errorf("图片URL长度必须为5-400")
		return nil, err
	}
	if len(req.Description) < util.LowerBoundOfFileDescriptionForMint || len(req.Description) > util.UpperBoundOfFileDescriptionForMint {
		err = errors.Errorf("图片描述长度必须为1-200")
		return nil, err
	}
	if req.MintAmount < util.LowerBoundOfMintAmountForMint || req.MintAmount > util.UpperBoundOfMintAmountForMint {
		err = errors.Errorf("每张图片铸造个数为1-5000")
		return nil, err
	}
	return collection, nil
}

// CheckMultipleMintParameter 同时铸造的参数校验
func (nc *MintController) CheckMultipleMintParameter(req common.MintMultipleReq) (*model.CollectionDetail, error) {
	if err := nc.CheckUserIdFromSession(req.UserId); err != nil {
		logger.Error(err.Error())
		nc.HandleErrCode(errcode.E.Base.ErrorMessage.WithMessage("无效用户ID"))
		return nil, err
	}
	//暂时只能管理员进行铸造
	userId, err := service.GetAdminId()
	if err != nil {
		logger.Error(err.Error())
		nc.HandleErrCode(errcode.E.Base.ErrorMessage.WithMessage("管理员查找出错"))
		return nil, err
	}
	if len(req.MintData) == 0 {
		nc.HandleErrCode(errcode.E.Base.ErrorMessage.WithMessage("请求铸造数据为空"))
		return nil, err
	}
	var reqCollection *model.CollectionDetail
	reqCollectionIndex := util.EmptyString
	for _, reqParameter := range req.MintData {
		//限制铸造次数
		collection, err := nc.CheckMintParameter(reqParameter, userId)
		if err != nil || collection == nil {
			nc.HandleErrCode(errcode.E.Base.ErrorMessage.WithMessage(err.Error()))
			return nil, err
		}
		if reqCollectionIndex == util.EmptyString {
			reqCollectionIndex = collection.CollectionIndex
			reqCollection = collection
		}
		if reqCollectionIndex != collection.CollectionIndex {
			err = errors.Errorf("所有图片只能同时铸造到同一个集合内")
			logger.Info(err.Error())
			nc.HandleErrCode(errcode.E.Base.ErrorMessage.WithMessage(err.Error()))
			return nil, err
		}
	}
	return reqCollection, nil
}

//一个集合同时铸造多张图片
//子集合分别构造不同的collection,属于同一个collectionIndex,不同的subCollectionIndex
func (nc *MintController) RequestMultipleMint() {
	//铸造请求头
	req := common.MintMultipleReq{}
	if err := json.Unmarshal(nc.Ctx.Input.RequestBody, &req); err != nil {
		logger.Error("RequestMultipleMint Unmarshal error: %v", err)
		nc.HandleErrCode(errcode.E.Base.RequestInvalid.WithErr(err))
		return
	}
	//参数校验
	collection, err := nc.CheckMultipleMintParameter(req)
	if err != nil || collection == nil {
		return
	}
	//加入铸造队列后直接返回
	err = service.WriteMintTaskIntoRedis(req, db.RedisPool)
	if err != nil {
		logger.Error("MintTask: Fail to insert mint task queue: %v", err)
		nc.HandleErrCode(errcode.E.Base.ErrorMessage.WithMessage("加入铸造队列失败"))
		return
	}
	nc.HandleBusinessSuccess()
}

func (nc *MintController) writeDestroyAssetsInfoToDatabase(collectionDetail *model.CollectionDetail, ownerId, operatorId, tokenId string) error {
	burnAssetInfo := &model.BurnAsset{
		CollectionIndex:    collectionDetail.CollectionIndex,
		SubCollectionIndex: collectionDetail.SubCollectionIndex,
		Owner:              ownerId,
		Operator:           operatorId,
		OperatingStatus:    service.ToBeBurntStatus,
		TokenId:            tokenId,
	}
	err := service.InsertBurnAsset(burnAssetInfo)
	if err != nil {
		logger.Error("BurnAssets: Fail to insert brun asset queue: %v", err)
		nc.HandleErrCode(errcode.E.Base.ErrorMessage.WithMessage("销毁失败"))
		return errcode.E.Base.ErrorMessage.WithMessage("销毁失败")
	}
	return nil
}

func (nc *MintController) BurnAssets() {
	//销毁请求头
	req := common.BurnAssetReq{}
	if err := json.Unmarshal(nc.Ctx.Input.RequestBody, &req); err != nil {
		logger.Error("RequestBurn Unmarshal error: %v", err)
		nc.HandleErrCode(errcode.E.Base.RequestInvalid.WithErr(err))
		return
	}

	adminInfo, err := service.GetAdmin()
	if err != nil {
		logger.Error("BurnAssets Get admin error: %v", err)
		nc.HandleErrCode(errcode.E.Base.ErrorMessage.WithMessage("获取权限信息失败"))
		return
	}

	err = nc.destroySetAsset(adminInfo, req.CollectionIndex, req.UserId, req.SubCollectionIndex)
	if err != nil {
		return //已经处理
	}
	return

}

func (nc *MintController) destroySetAsset(adminInfo *model.User, collectionIndex string, userId, subCollectionIndex int) error {
	if userId != adminInfo.Id {
		nc.HandleErrCode(errcode.E.Base.ErrorMessage.WithMessage("普通用户不可销毁集合"))
		return errcode.E.Base.ErrorMessage.WithMessage("普通用户不可销毁集合")
	}
	if subCollectionIndex == 0 { // 全部销毁
		err := nc.checkAllSubCollectionCanBeDestroyedAndWriteToQueue(adminInfo, collectionIndex)
		if err != nil {
			return err // 已经处理
		}
	} else { //销毁单个集合
		subCollectionDetail, err := service.GetCollectionDetailById(collectionIndex, subCollectionIndex)
		if err != nil {
			logger.Error("destroySetAsset GetCollectionDetailById error: %v", err)
			nc.HandleErrCode(errcode.E.Base.ErrorMessage.WithMessage("获取藏品信息失败！"))
			return errcode.E.Base.ErrorMessage.WithMessage("获取藏品信息失败！")
		}
		canBurn, err := service.JudgeAndUpdateStatus(subCollectionDetail)
		if err != nil {
			logger.Error("BurnAssets: JudgeAndUpdateStatus error: %v", err)
			nc.HandleErrCode(errcode.E.Base.ErrorMessage.WithMessage("销毁失败！"))
			return errcode.E.Base.ErrorMessage.WithMessage("销毁失败！")
		}
		if !canBurn {
			nc.HandleErrCode(errcode.E.Base.ErrorMessage.WithMessage("当前集合正在进行销毁"))
			return errcode.E.Base.ErrorMessage.WithMessage("当前集合正在进行销毁")
		}
		err = nc.writeDestroyAssetsInfoToDatabase(subCollectionDetail, cast.ToString(adminInfo.Id), cast.ToString(adminInfo.Id), "")
		if err != nil {
			return err
		}
	}
	msg := " 销毁请求发送成功，稍后可在集合状态中查看"

	resp := common.BurnAssetResp{
		Message: msg,
	}
	nc.HandleBusinessObj(resp)
	return nil
}

func (nc *MintController) checkAllSubCollectionCanBeDestroyedAndWriteToQueue(adminInfo *model.User, CollectionIndex string) error { //辅助函数 检查所有的子集合是否都可销毁 并写入收据库
	subCollectionDetails, err := service.GetCollectionsByOneCollectionIndex(CollectionIndex)
	if err != nil {
		logger.Error("destroySetAsset GetCollectionDetailById error: %v", err)
		nc.HandleErrCode(errcode.E.Base.ErrorMessage.WithMessage("获取藏品信息失败！"))
		return errcode.E.Base.ErrorMessage.WithMessage("获取藏品信息失败！")
	}
	for _, subCollectionDetail := range subCollectionDetails {
		if subCollectionDetail.CollectionStatus != util.CollectionBurning && subCollectionDetail.CollectionStatus != util.CollectionMintCompleted { //校验是否可销毁
			nc.HandleErrCode(errcode.E.Base.ErrorMessage.WithMessage("该状态不可销毁！"))
			return errcode.E.Base.ErrorMessage.WithMessage("该状态不可销毁！")
		}
	}
	for _, subCollectionDetail := range subCollectionDetails {
		canBurn, err := service.JudgeAndUpdateStatus(subCollectionDetail)
		if err != nil {
			logger.Error("BurnAssets: JudgeAndUpdateStatus error: %v", err)
			nc.HandleErrCode(errcode.E.Base.ErrorMessage.WithMessage("销毁失败！"))
			return errcode.E.Base.ErrorMessage.WithMessage("销毁失败！")
		}
		if !canBurn {
			continue
		}
		if subCollectionDetail.SubCollectionIndex == util.DefaultSubCollectionIndex {
			continue
		}
		err = nc.writeDestroyAssetsInfoToDatabase(subCollectionDetail, cast.ToString(adminInfo.Id), cast.ToString(adminInfo.Id), "")
		if err != nil {
			//已经处理
			return err
		}
	}
	return nil
}
